package com.lsh.oms.core.service.payment;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lsh.oms.core.constant.ReturnCode;
import com.lsh.oms.core.dao.order.OrderDetailDao;
import com.lsh.oms.core.dao.order.OrderHeadDao;
import com.lsh.oms.core.dao.order.OrderHeadExtDao;
import com.lsh.oms.core.dao.order.OrderOperateLogDao;
import com.lsh.oms.core.enums.*;
import com.lsh.oms.core.exception.BusinessException;
import com.lsh.oms.core.model.order.*;
import com.lsh.oms.core.redis.RedisTemplate;
import com.lsh.oms.core.service.order.OrderUpdateService;
import com.lsh.oms.core.service.other.ShippingOrderService;
import com.lsh.oms.core.service.other.SignOrderService;
import com.lsh.oms.core.utils.IdGenerator;
import com.lsh.oms.core.utils.OMSUtils;
import com.lsh.oms.core.utils.ShopCheckUtils;
import com.lsh.oms.core.utils.http.HttpClientTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Project Name: PaymentService
 * 北京链商电子商务有限公司
 *
 * @author wangliutao
 * Date: 18/5/29
 * Package Name: com.lsh.oms.core.service.payment
 * Description:
 */
@Service
public class PaymentService {
    private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);
    /**
     * //系统取消,通知商城;商城判断是否需要退券
     */
    private static final String SHOP_ORDER_CANCEL_URL = "/cal/index/orderSystemCancel";
    private static final String ORDER_PAY_LOCK = "order:pay:lock:{0}";
    private static final Long ORDER_PAY_BUFFER = 300L;
    private static final String LOCK = "lock";

    @Value("${mis.host}")
    private String shopHost;

    @Autowired
    private OrderHeadDao orderHeadDao;

    @Autowired
    private OrderDetailDao orderDetailDao;

    @Autowired
    private OrderOperateLogDao orderOperateLogDao;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderHeadExtDao orderHeadExtDao;

    @Autowired
    private SignOrderService signOrderService;

    @Autowired
    private ShippingOrderService shippingOrderService;

    @Autowired
    private OrderUpdateService orderUpdateService;

    @Resource
    private SignOrderService signOrders;

    /**
     * 如果该订单是品类仓父单,则把子单查出来,放到orderHeads中,进行接下来的逻辑操作。
     * ( 品类仓在线支付, 支付回调的逻辑: 将父单置为无效,将无效的子单置为有效;
     * 品类仓在线支付, 取消订单的逻辑: 用子单去还原库存 )
     *
     * @param head
     */
    public List<OrderHead> getCategorySubOrdersIfParent(OrderHead head) {
        ArrayList<OrderHead> orderHeads = new ArrayList<>(4);

        String misOrderNo = head.getMisOrderNo();
        JSONObject ext = JSON.parseObject(head.getExt());
        if (ext.getString("isMisParentNo") != null && ext.getString("isMisParentNo").equals("1") && head.getParentOrderCode().longValue() != 0L) {
            List<OrderHead> heads = this.getOrderHeads(head.getParentOrderCode(), true);
            for (OrderHead oh : heads) {
                JSONObject json = JSON.parseObject(oh.getExt());
                String misParentOrderNo = json.getString("misParentOrderNo");
                if (misParentOrderNo != null && !misParentOrderNo.equals("0")) {
                    if (misOrderNo.equals(misParentOrderNo)) {
                        orderHeads.add(oh);
                    }
                }
            }
        }
        return orderHeads;
    }

    /**
     * 如果该订单是品类仓子单,则把同一个品类仓父单下的所有子单查出来，判断同一个品类仓父单下的子单是否都已经取消，如果是，则用品类仓父单通知商城退券。
     *
     * @param head
     */
    public void notifyShopIfCategorySubOrdersAllCancel(OrderHead head, String logSign) {
        JSONObject ext = JSON.parseObject(head.getExt());
        if (ext == null) {
            return;
        }

        String misParentOrderNo = ext.getString("misParentOrderNo");
        if (misParentOrderNo == null || misParentOrderNo.equals("0") || head.getParentOrderCode().longValue() == 0L) {
            return;
        }

        OrderHead orderHead = null;
        List<OrderHead> heads = this.getOrderHeads(head.getParentOrderCode(), true);
        for (OrderHead oh : heads) {
            JSONObject json = JSON.parseObject(oh.getExt());
            String parentOrderNo = json.getString("misParentOrderNo");
            if (parentOrderNo != null && parentOrderNo.equals(misParentOrderNo)) {
                if (oh.getOrderStatus().compareTo(OrderStatus.ORDER_CANCEL_CUSTOMERSERVICE.getIndex()) < 0) {
                    return;
                }
            } else if (oh.getMisOrderNo().equals(misParentOrderNo)) {
                orderHead = oh;
            }
        }

        if (orderHead == null) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "当前子单，查不到父订单信息，orderCode：" + head.getOrderCode());
        }
        this.orderCancelNotifyShop(Collections.singletonList(orderHead.getOrderCode()), logSign, orderHead.getVenderId());
    }

    @Transactional(readOnly = true)
    public OrderHeadExt getOrderHeadExt(OrderHead orderHead) {
        return orderHeadExtDao.get(orderHead.getId());
    }

    @Transactional(readOnly = true)
    public List<OrderHead> getOrderHeadsByOrderCodes(List<Long> orderCodes) {
        return orderHeadDao.findListByOrderCodes(orderCodes);
    }

    @Transactional(readOnly = true)
    public List<OrderHead> getOrderHeadsByOrderCodesWithDetails(List<Long> orderCodes) {
        List<OrderHead> orderHeads = orderHeadDao.findListByOrderCodes(orderCodes);
        for (OrderHead orderHead : orderHeads) {
            OrderDetail param = new OrderDetail();
            param.setOrderCode(orderHead.getOrderCode());
            List<OrderDetail> orderDetails = orderDetailDao.findList(param);
            orderHead.setDetails(orderDetails);
        }

        return orderHeads;
    }

    @Transactional(readOnly = true)
    public List<OrderHead> getOrderHeads(Long orderCode, boolean isParentOrderCode) {
        OrderHead filter = new OrderHead();
        if (isParentOrderCode) {
            filter.setParentOrderCode(orderCode);
        } else {
            filter.setOrderCode(orderCode);
        }
        return orderHeadDao.findList(filter);
    }

    @Transactional(readOnly = true)
    public OrderHead getOrderHead(Long orderCode) {
        OrderHead filter = new OrderHead();
        filter.setOrderCode(orderCode);

        return orderHeadDao.findOne(filter);
    }


    @Transactional(readOnly = true)
    public int countByAddressCode(Long addressCode) {
        return orderHeadDao.countByAddressCode(addressCode, OMSUtils.currentDate());
    }

    @Transactional(rollbackFor = Exception.class)
    public void updatePayStatusBatch(List<OrderHead> heads, OrderHead orderHead) {
        for (OrderHead head : heads) {
            if (head.getValid().intValue() != 0) {
                orderHead.setActualPayAmount(head.getExpectPayAmount());
                orderHead.setId(head.getId());
                orderHead.setValid(head.getValid());
                orderHeadDao.update(orderHead);
            } else {
                OrderHead head1 = new OrderHead();
                head1.setId(head.getId());
                head1.setValid(head.getValid());
                head1.setPayStatus(PayStatus.PAID.getValue());
                head1.setOrderStatus(orderHead.getOrderStatus());
                orderHeadDao.update(head1);
            }
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public void updatePayStatus(OrderHead orderHead, boolean isParentOrderCode) {
        orderHead.setUpdateTime((int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));

        OrderHead filter = new OrderHead();
        filter.setValid(1);
        if (isParentOrderCode) {
            filter.setParentOrderCode(orderHead.getParentOrderCode());
        } else {
            filter.setOrderCode(orderHead.getOrderCode());
        }
        orderHeadDao.updateByFilter(orderHead, filter);
    }

    @Transactional(rollbackFor = Exception.class)
    public void orderTimeoutCancelAndNotfyShop(OrderHead orderHeadForUpdate, Long venderId) {
        OrderHead filter = new OrderHead();
        filter.setId(orderHeadForUpdate.getId());
        filter.setOrderCode(orderHeadForUpdate.getOrderCode());
        filter.setOrderStatus(OrderStatus.ORDER_NEW.getIndex());
        long headId = orderHeadForUpdate.getId();
        orderHeadForUpdate.setId(null);
        int updateFlag = orderHeadDao.updateByFilter(orderHeadForUpdate, filter);

        if (updateFlag > 0) {
            this.cancelOrderExt(headId);

            this.orderCancelNotifyShop(Collections.singletonList(orderHeadForUpdate.getOrderCode()), "[订单支付超时]", venderId);
        }

    }

    private void cancelOrderExt(Long headId) {
        // TODO 2019-05-09
        try {
            OrderHeadExt headExt = new OrderHeadExt();
            headExt.setId(headId);
            headExt.setCancelTime(new Date());
//            headExt.setCancelUser("");
            headExt.setCancelRemark("订单未支付取消");
            logger.info(headId + "订单未支付取消 {}", JSON.toJSONString(headExt));
            this.orderHeadExtDao.update(headExt);
        } catch (Exception e) {
            logger.error("未支付取消订单设置 ext", e);
        }
    }

    /**
     * @param orderCodes
     * @param logSign
     */
    public void orderCancelNotifyShop(List<Long> orderCodes, String logSign, Long venderId) {
        if (orderCodes == null || orderCodes.size() == 0) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "orderCodes为空");
        }
        String apiUrl = shopHost + SHOP_ORDER_CANCEL_URL;

        // 如果用户下单使用了优惠券,通知商城退券
        Map<String, String> head = new HashMap<>(6);
        head.put("api-version", "1.1");
        head.put("sign", String.valueOf(IdGenerator.genId()));
        head.put("platform", "oms");
        head.put("venderId", venderId + "");
        HashMap<String, Object> param = new HashMap<>(2);
        param.put("order_id", orderCodes);

        logger.info("[{}] 通知商城, url: {}", logSign, apiUrl);
        logger.info("[{}] 通知商城, 请求参数: {}", logSign, JSON.toJSONString(param));
        String resp = HttpClientTemplate.doPostBody(apiUrl, JSON.toJSONString(param), head);
        logger.info("[{}] 通知商城响应: {}", logSign, resp);

        JSONObject result = JSON.parseObject(resp);
        JSONObject content = result.getJSONObject("content");
        Integer ret = result.getInteger("ret");
        if (ret != 0 || content.isEmpty()) {
            logger.info("[{}] 通知商城失败, msg:{}", logSign, content);
            throw new BusinessException(ReturnCode.REQUEST_FAIL, logSign + " 通知商城失败! " + result.getString("msg"));
        }
        JSONArray jsonArray = content.getJSONArray("fail");
        if (jsonArray != null && jsonArray.size() != 0) {
            logger.info("[{}] 通知商城失败, orderCode: {}", logSign, jsonArray);
            throw new BusinessException(ReturnCode.REQUEST_FAIL, logSign + " 通知商城失败! " + jsonArray);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void addOperateLog(OrderHead orderHead, OrderOperateType operateType, Integer status) {
        int timestamp = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        OrderOperateLog operateLog = new OrderOperateLog();
        operateLog.setOrderCode(orderHead.getOrderCode());
        operateLog.setOrderType(OperateOrderType.ORDER.getValue());
        operateLog.setOperateType(operateType.getValue());
        operateLog.setOperateDesc(new StringBuilder(operateType.getDesc()).append("，订单状态从`").append(OrderStatus.getName(orderHead.getOrderStatus())).append("`更新为`").append(OrderStatus.getName(status)).append("`").toString());
        operateLog.setOperateTime(timestamp);
        operateLog.setCreateTime(timestamp);
        operateLog.setVenderId(orderHead.getVenderId());
        this.orderOperateLogDao.insert(operateLog);
    }

    @Transactional(readOnly = true)
    public int countByParentOrderCode(Long parentOrderCode) {
        OrderHead param = new OrderHead();
        param.setParentOrderCode(parentOrderCode);
        param.setValid(1);
        return orderHeadDao.count(param);
    }

    public boolean lock(final Long orderCode) {
        if (redisTemplate.exists(this.getOrderPayLockKey(orderCode))) {
            logger.info("update lock time! orderCode: {}", orderCode);
            return redisTemplate.set(this.getOrderPayLockKey(orderCode), PaymentService.LOCK, true, PaymentService.ORDER_PAY_BUFFER, TimeUnit.SECONDS) != null;
        } else {
            logger.info("create lock! orderCode: {}", orderCode);
            return redisTemplate.set(this.getOrderPayLockKey(orderCode), PaymentService.LOCK, false, PaymentService.ORDER_PAY_BUFFER, TimeUnit.SECONDS) != null;
        }
    }


    public OrderHead initOrderHead4update(String channelType, BigDecimal payAmount, String payPaymentNo, OrderHead orderHead) {

        int time = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        OrderHead orderHead4update = new OrderHead();
        //支付流水号
        orderHead4update.setPaySequence(payPaymentNo);
        orderHead4update.setPayChannel(Integer.parseInt(channelType));

        if (!channelType.equals(PayChannel.BALANCE_PAYMENT.getCode())) {
            orderHead4update.setActualPayAmount(payAmount);
        } else {
//            orderHead4update.setActualPayAmount();
        }
        //0  未支付  1  已支付
        orderHead4update.setPayStatus(PayStatus.PAID.getValue());
        orderHead4update.setPayTime(time);
        orderHead4update.setUpdateTime(time);

        boolean passFlag = false;
        try {
            passFlag = ShopCheckUtils.checkMarketInfo4Position(orderHead.getAddressInfo());
        } catch (Throwable throwable) {
            logger.error("check market info for position error!!", throwable);
        }
        if (passFlag) {
//            if (orderHead.getRegionCode() != 2000 && orderHead.getRegionCode() != 5000) {
//                boolean first = this.countByAddressCode(orderHead.getAddressCode()) <= 1;
//                if (first) {
            orderHead4update.setOrderStatus(OrderStatus.ORDER_SPLIT_CHECK.getIndex());
//                } else {
//                    orderHead4update.setOrderStatus(OrderStatus.ORDER_NO_EDIT.getIndex());
//                }
//            } else {
//                orderHead4update.setOrderStatus(OrderStatus.ORDER_SPLIT_CHECK.getIndex());
//            }
        } else {
            orderHead4update.setOrderStatus(OrderStatus.ORDER_NO_EDIT.getIndex());
        }

        return orderHead4update;
    }

    public boolean unLock(final Long orderCode) {
        return redisTemplate.unlock(this.getOrderPayLockKey(orderCode));
    }

    public boolean exists(final Long orderCode) {
        return redisTemplate.exists(this.getOrderPayLockKey(orderCode));
    }

    private String getOrderPayLockKey(Long orderCode) {
        return MessageFormat.format(PaymentService.ORDER_PAY_LOCK, String.valueOf(orderCode));
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public boolean updatePayStatus(boolean directDelivery, OrderSignHead sign, OrderHead orderHead, OrderShippingHead shippingHead, OrderSignHead forUpdate) {

        int orderFlag = orderUpdateService.updateOrderStatus2(orderHead);
        if (orderFlag != 1) {
            return false;
        }

        boolean shipppingFlag = shippingOrderService.updateStatus(shippingHead);
        if (!shipppingFlag) {
            return false;
        }

        boolean success = signOrderService.updateSignPayStatus(forUpdate);
        if (success) {
            signOrders.createReturnRequest(directDelivery, sign, Collections.emptyMap());
            signOrders.createIssueTask(sign);
        }
        return success;
    }


}
